#include "rectangle.h"
#include "freefuncs.h"

//int Rectangle::subRectNum (const Coordinate& c) const
//{
//	if (nothingOnRow(c) || nothingOnCol(c))
//		return 2;
//	else if
//}

void Rectangle::divide (const Coordinate& coo, Rectangle& rT, Rectangle& rTR, Rectangle& rR, Rectangle& rBR,
		Rectangle& rB, Rectangle& rBL, Rectangle& rL, Rectangle& rTL) const
{
	if (!this->cooIsInside(coo))
		return; //nothing to be done if the rectangle does not surround the coo
	//vector<Coordinate>::iterator it = this->coos.begin();
	//for (; it != this->coos.end(); it++);
	for (int i = 0; i < this->gettotalCoo(); i++)
		switch (coo.whichSideIs(this->coos[i]))
		{
		case rlTop:
			rT.addCoo(this->coos[i]);
			break;
		case rlTopRight:
			rTR.addCoo(this->coos[i]);
			break;
		case rlRight:			
			rR.addCoo(this->coos[i]);
			break;
		case rlBottomRight:
			rBR.addCoo(this->coos[i]);
			break;
		case rlBottom:
			rB.addCoo(this->coos[i]);
			break;
		case rlBottomLeft:
			rBL.addCoo(this->coos[i]);
			break;
		case rlLeft:
			rL.addCoo(this->coos[i]);
			break;
		case rlTopLeft:
			rTL.addCoo(this->coos[i]);
			break;
		case rlInside:
			cout << "Error: minor coo is exactly on a major coo. Error from Rectangle::divide();\n";
	}
}

void Rectangle::addRect (const Rectangle& other)
{
	int newT, newR, newB, newL; //the new limits of the rectangle
	for (int i = 0; i<other.gettotalCoo(); i++) //first we add all the coos of the other
		this->addCoo(other.coos[i]);
	newT = this->getTop() < other.getTop()? this->getTop() : other.getTop(); //then we enlarge this
	newR = this->getRight() < other.getRight()? other.getRight() : this->getRight();
	newB = this->getBottom() < other.getBottom()? other.getBottom() : this->getBottom();
	newL = this->getLeft() < other.getLeft()? this->getLeft() : other.getLeft();
	this->ul.col = newL;
	this->ul.row = newT;
	this->lr.col = newR;
	this->lr.row = newB;
}

int Rectangle::minDistance (const Coordinate& c) const
{
	switch (this->whichSideIs(c))
	{
	case rlInside:
		return 0;
	case rlTop:
		return (this->getTop() - c.row);
	case rlTopRight:
		return (this->getTop() - c.row) + (c.col - this->getRight());
	case rlRight:
		return (c.col - this->getRight());
	case rlBottomRight:
		return (c.col - this->getRight()) + (c.row - this->getBottom());
	case rlBottom:
		return (c.row - this->getBottom());
	case rlBottomLeft:
		return (c.row - this->getBottom()) + (this->getRight() - c.row);
	case rlLeft:
		return (this->getLeft() - c.col);
	case rlTopLeft:
		return (this->getLeft() - c.col) + (this->getTop() - c.row);
	default:
		cout << "Error calculating Rectangle::minDistance(). Where the heck is the given point??";
		return -1;
	}
}


void Rectangle::addCoo (const Coordinate& coo)
{
	if (this->gettotalCoo() == 0) //just add the coo if the rectangle is empty.
	{
		this->coos.push_back(coo);
		this->setLR(coo);
		this->setUL(coo);
		return;
	}
	//first check to see if the coo already exists in the rectangle's coos
	vector<Coordinate>::iterator it = this->coos.begin();
	for (; it != this->coos.end(); it++)
		if (it->isEqual(coo))
			return;

	this->coos.push_back(coo);
	switch (this->whichSideIs(coo))
	{
	case rlInside:
		break;
	case rlTopLeft:
		this->setUL(coo);
		break;
	case rlTop:
		this->setUL_Row(coo.row);
		break;
	case rlTopRight:
		this->setUL_Row(coo.row);
		this->setLR_Col(coo.col);
		break;
	case rlRight:
		this->setLR_Col(coo.col);
		break;
	case rlBottomRight:
		this->setLR(coo);
		break;
	case rlBottom:
		this->setLR_Row(coo.row);
		break;
	case rlBottomLeft:
		this->setLR_Row(coo.row);
		this->setUL_Col(coo.col);
		break;
	case rlLeft:
		this->setUL_Col(coo.col);
		break;
	default:
		cout << "Error: the point to be added to the rectangle has" <<
			"invalid relative coordinate. Message from Rectangle::addCoo()" << endl;
	}
}

void Rectangle::addCooNoCheck (const Coordinate& coo)
{
	if (this->gettotalCoo() == 0) //just add the coo if the rectangle is empty.
	{
		this->coos.push_back(coo);
		this->setLR(coo);
		this->setUL(coo);
		return;
	}
	
	this->coos.push_back(coo);
	switch (this->whichSideIs(coo))
	{
	case rlInside:
		break;
	case rlTopLeft:
		this->setUL(coo);
		break;
	case rlTop:
		this->setUL_Row(coo.row);
		break;
	case rlTopRight:
		this->setUL_Row(coo.row);
		this->setLR_Col(coo.col);
		break;
	case rlRight:
		this->setLR_Col(coo.col);
		break;
	case rlBottomRight:
		this->setLR(coo);
		break;
	case rlBottom:
		this->setLR_Row(coo.row);
		break;
	case rlBottomLeft:
		this->setLR_Row(coo.row);
		this->setUL_Col(coo.col);
		break;
	case rlLeft:
		this->setUL_Col(coo.col);
		break;
	default:
		cout << "Error: the point to be added to the rectangle has" <<
			"invalid relative coordinate. Message from Rectangle::addCoo()" << endl;
	}
}


Rectangle Rectangle::virtualExpand (const Coordinate& coo) const
{
	Rectangle copy(*this);
	if (copy.gettotalCoo() == 0) //just add the coo if the rectangle is empty.
	{
		copy.coos.push_back(coo);
		copy.setLR(coo);
		copy.setUL(coo);
		return copy;
	}
	//first check to see if the coo already exists in the rectangle's coos
	vector<Coordinate>::iterator it = copy.coos.begin();
	for (; it != copy.coos.end(); it++)
		if (it->isEqual(coo))
			return copy;

	copy.coos.push_back(coo);
	switch (copy.whichSideIs(coo))
	{
	case rlInside:
		break;
	case rlTopLeft:
		copy.setUL(coo);
		break;
	case rlTop:
		copy.setUL_Row(coo.row);
		break;
	case rlTopRight:
		copy.setUL_Row(coo.row);
		copy.setLR_Col(coo.col);
		break;
	case rlRight:
		copy.setLR_Col(coo.col);
		break;
	case rlBottomRight:
		copy.setLR(coo);
		break;
	case rlBottom:
		copy.setLR_Row(coo.row);
		break;
	case rlBottomLeft:
		copy.setLR_Row(coo.row);
		copy.setUL_Col(coo.col);
		break;
	case rlLeft:
		copy.setUL_Col(coo.col);
		break;
	default:
		cout << "Error: the point to be added to the rectangle has" <<
			"invalid relative coordinate. Message from Rectangle::addCoo()" << endl;
	}
	return copy;
}

bool Rectangle::hasCoo (const Coordinate& c) const
{
	vector<Coordinate>::const_iterator it = coos.begin();
	while (it->isNotEqual(c) && it != coos.end()) 
		it++;
	if (it == coos.end())
		return false;
	else
		return true;
}

bool Rectangle::coversAnyCoo (const vector<Coordinate>& coos) const
{
	for (vector<Coordinate>::const_iterator cooIt = coos.begin(); cooIt != coos.end(); ++cooIt)
	{
		if (cooIsInside(*cooIt))
			return true;
	}
	return false;
}

int Rectangle::coveredCooNum (const vector<Coordinate>& coos) const
{
	int result = 0;
	for (vector<Coordinate>::const_iterator cooIt = coos.begin(); cooIt != coos.end(); ++cooIt)
	{
		if (cooIsInside(*cooIt))
			++result;
	}
	return result;
}

vector<Rectangle> Rectangle::breakRectangleToExcludeCoos (const vector<Coordinate>& coos) const
{
	vector<Rectangle> raw, complete;
	Rectangle currentRect1, halfRect1, halfRect2;
	raw.push_back(*this); //initiating raw. needs to have at least one object.
	while (raw.size() > 0)
	{
		currentRect1 = raw.back();
		raw.pop_back();
		if (!currentRect1.coversAnyCoo(coos))
			complete.push_back(currentRect1);
		else
		{
			currentRect1.divideToTwoWithMinCooErrors(coos, halfRect1, halfRect2);

			if (halfRect1.gettotalCoo() > 0)
				raw.push_back(halfRect1);
			if (halfRect2.gettotalCoo() > 0)
				raw.push_back(halfRect2);
		}
	}
	return complete;
}

bool Rectangle::divideToTwoWithMinCooErrors (const vector<Coordinate>& coos, Rectangle& halfRect1, Rectangle& halfRect2, bool reportToConsole) const
{
	halfRect1.clearCoos();
	halfRect2.clearCoos();
	halfRect1.longinstance.clear();
	halfRect2.longinstance.clear();
	assert(coveredCooNum(coos) && "The given Rectangle2 is already error free. No conflict to be resolved.");
	int totalCoo = gettotalCoo();
	if (reportToConsole)
	{
		cout << "\ndivideToTwoWithMinCooErrors() called with " << totalCoo << " coos.";
	}
	if (totalCoo >= 32) //It can go up to 64 but the computation time takes forever. I set it to 32 for getting some results for now.
	{
		assert("Too many coordinates in the Rectangle2, out of computing power.");
		return false;
	}
	unsigned __int64 bestDivisionIndex = 0;
	Rectangle best1, best2;
	int minErrors = totalCoo + 1;
	bool* label = new bool[totalCoo];
	int totalMajorsInsideEither;
	unsigned __int64 totalCombinations = twoToThePowerOf(totalCoo);
	for (unsigned __int64 i = 1; i < totalCombinations; i++)
	{
		if (reportToConsole)
		{
			cout << "Doing " << totalCombinations << " combinations. ";
			if (i % 1000000 == 0)
				cout << "  " << i/1000000 << "/" << totalCombinations/1000000 << "...";
		}
		best1.clearCoos();
		best2.clearCoos();
		//RuletangleHelperFuncs::binary(i, totalCoo, label);
		longBinary(i, totalCoo, label);
		for (int j = 0; j < totalCoo; j++)
			if (label[j] == 0)
				best1.addCooNoCheck(getCoo(j));
			else
				best2.addCooNoCheck(getCoo(j));
		
		totalMajorsInsideEither = best1.coveredCooNum(coos) + best2.coveredCooNum(coos);
		if (totalMajorsInsideEither < minErrors)
		{
			minErrors = totalMajorsInsideEither;
			bestDivisionIndex = i;
		}
		if (minErrors == 0)
			break;
	}
	if (reportToConsole)
		cout << endl;
	longBinary (bestDivisionIndex, totalCoo, label);
	for (int j = 0; j < totalCoo; j++)
			if (label[j] == 0)
				halfRect1.addCooNoCheck(getCoo(j));
			else
				halfRect2.addCooNoCheck(getCoo(j));
	delete[] label;
	//TODO: keep the best rectangles somewhere and use opertor= for t1 and t2 here to speed up.
	return true;
}

bool Rectangle::divideToTwoNotCoveringcoo (const Coordinate& coo, Rectangle& halfRect1, Rectangle& halfRect2) const
{
	assert(cooIsInside(coo) && "The given coo is already not in this rectangle.");
	assert (halfRect1.gettotalCoo() == 0 && halfRect2.gettotalCoo() == 0 && "The given halfrectangles are not empty.");
	
	if (!hasCooOnRow(coo.row))
	{
		//divide to bellow and above the line
		for (vector<Coordinate>::const_iterator cooIT = this->coos.begin(); cooIT != coos.end(); ++cooIT)
		{
			if (cooIT->row < coo.row)
				halfRect1.addCooNoCheck(*cooIT);
			else
				halfRect2.addCooNoCheck(*cooIT);
		}
		return true;
	}
	
	if (!hasCooOnCol(coo.col))
	{
		//divide to left and right of the line
		for (vector<Coordinate>::const_iterator cooIT = this->coos.begin(); cooIT != coos.end(); ++cooIT)
		{
			if (cooIT->col < coo.col)
				halfRect1.addCooNoCheck(*cooIT);
			else
				halfRect2.addCooNoCheck(*cooIT);
		}
		return true;
	}

	return false;
}

bool Rectangle::hasCooOnRow(int row) const
{
	for (vector<Coordinate>::const_iterator cooIT = coos.begin(); cooIT != coos.end(); ++cooIT)
	{
		if (cooIT->row == row)
			return true;
	}
	return false;
}

bool Rectangle::hasCooOnCol(int col) const
{
	for (vector<Coordinate>::const_iterator cooIT = coos.begin(); cooIT != coos.end(); ++cooIT)
	{
		if (cooIT->row == col)
			return true;
	}
	return false;
}

vector<Coordinate> Rectangle::getThoseCoosInsideOnly (const vector<Coordinate>& totalCoos) const
{
	vector<Coordinate> result;
	for (vector<Coordinate>::const_iterator cooIT = totalCoos.begin(); cooIT != totalCoos.end(); ++cooIT)
	{
		if (cooIsInside(*cooIT))
			result.push_back(*cooIT);
	}
	return result;
}

float Rectangle::minDistance (const Rectangle& other) const
{
	float minDis = 0.0;

	return minDis;
}

bool Rectangle::cooIsInside (const Coordinate& c) const
{
	return (c.col >= this->ul.col && c.col <= this->lr.col
		&& c.row >= this->ul.row && c.row <= this->lr.row);
}


bool Rectangle::cooIsOnLeft (const Coordinate& coo) const
{
	return (coo.col < this->ul.col);
}

bool Rectangle::cooIsOnRight (const Coordinate& coo) const
{
	return (coo.col > this->lr.col);
}

bool Rectangle::cooIsOnTop (const Coordinate& coo) const
{
	return (coo.row < this->ul.row);
}

bool Rectangle::cooIsOnButtom (const Coordinate& coo) const
{
	return (coo.row > this->lr.row);
}

relativeLocation Rectangle::whichSideIs (const Coordinate& coo) const
{
	relativeLocation result;
	if (cooIsInside (coo))
		result = rlInside;
	else if (cooIsOnTop(coo))
		if (cooIsOnLeft(coo))
			result = rlTopLeft;
		else if (cooIsOnRight(coo))
			result = rlTopRight;
		else
			result = rlTop;
	else if (cooIsOnButtom(coo))
		if (cooIsOnLeft(coo))
			result = rlBottomLeft;
		else if (cooIsOnRight(coo))
			result = rlBottomRight;
		else
			result = rlBottom;
	else
		if (cooIsOnLeft(coo))
			result = rlLeft;
		else if (cooIsOnRight(coo))
			result = rlRight;
	return result;
}

Rectangle::Rectangle (const Coordinate& coo)
{
	this->setUL(coo);
	this->setLR(coo);
	this->coos.clear();
	this->coos.push_back(coo);
	//this->longinstance = null;
}

Rectangle::Rectangle (const Coordinate& coo, const LongInstance& li)
{
	this->setUL(coo);
	this->setLR(coo);
	this->coos.clear();
	this->coos.push_back(coo);
	this->longinstance = LongInstance(li);
}

Rectangle::Rectangle (const Coordinate& ul, const Coordinate& lr)
{
	this->setUL(ul);
	this->setLR(lr);
}

Rectangle::Rectangle (const Coordinate& ul, const Coordinate& lr, const LongInstance& li)
{
	this->setUL(ul);
	this->setLR(lr);
	this->longinstance = LongInstance (li);
}

Rectangle::Rectangle (const Rectangle& other)
{
	this->setLR(other.lr);
	this->setUL(other.ul);
	this->longinstance = LongInstance (other.longinstance);
	for (int i = 0; i < (int) other.coos.size(); i++)
		this->coos.push_back(other.coos[i]);

	//vector<Coordinate>::const_iterator it;
	//for (it = other.coos.begin(); it != other.coos.end(); ++it)
	//	this->coos.push_back(it);
}

Rectangle::Rectangle (const LongInstance* li)
{
	if (li != NULL)
		this->longinstance = *li;
	if (li->getCooNum() == 0)
	{
		this->setLR(Coordinate(0,0));
		this->setUL(Coordinate(0,0));
		this->coos.clear();
		this->longinstance = *li;
		return;
	}

	if (li->getCooNum() == 1)
	{
		this->setLR(li->getCoordinate(0));
		this->setUL(li->getCoordinate(0));
		this->coos.push_back(li->getCoordinate(0));
		this->longinstance = *li;
		return;
	}

	this->longinstance = *li;
	this->setLR(li->getCoordinate(0));
	this->setUL(li->getCoordinate(0));
	this->coos.push_back(li->getCoordinate(0));

	Coordinate currentCoo;
	
	for (int i = 1; i < li->getCooNum(); i++)
	{
		currentCoo = li->getCoordinate(i);
		this->coos.push_back(currentCoo);
		if (currentCoo.row < this->getTop())
			this->setTop(currentCoo.row);
		if (currentCoo.row > this->getBottom())
			this->setBottom(currentCoo.row);
		if (currentCoo.col < this->getLeft())
			this->setLeft(currentCoo.col);
		if (currentCoo.col > this->getRight())
			this->setRight(currentCoo.col);
	}

}



emptyDirection Rectangle::emptyLine (const Coordinate& c) const
{
	emptyDirection result = toNone;
	bool upIsEmpty = true, rightIsEmpty = true, downIsEmpty = true, leftIsEmpty = true;
	vector<Coordinate>::const_iterator it;
	
	it = coos.begin();
	while (upIsEmpty && it != coos.end())
	{
		if (it->col == c.col && it->row < c.row)
			upIsEmpty = false;
		it++;
	}
	
	it = coos.begin();
	while (rightIsEmpty && it != coos.end())
	{
		if (it->row == c.row && it->col > c.col)
			rightIsEmpty = false;
		it++;
	}

	it = coos.begin();
	while (downIsEmpty && it != coos.end())
	{
		if (it->col == c.col && it->row > c.row)
			downIsEmpty = false;
		it++;
	}

	it = coos.begin();
	while (leftIsEmpty && it != coos.end())
	{
		if (it->row == c.row && it->col < c.col)
			leftIsEmpty = false;
		it++;
	}

	if (upIsEmpty)
		if (rightIsEmpty)
			if (downIsEmpty)
				if (leftIsEmpty)
					result = toUpRightDownLeft;
				else
					result = toUpRightDown;
			else
				if (leftIsEmpty)
					result = toUpRightLeft;
				else
					result = toUpRight;
		else
			if (downIsEmpty)
				if (leftIsEmpty)
					result = toUpDownLeft;
				else
					result = toUpDown;
			else
				if (leftIsEmpty)
					result = toUpLeft;
				else
					result = toUp;
	else
		if (rightIsEmpty)
			if (downIsEmpty)
				if (leftIsEmpty)
					result = toRightDownLeft;
				else
					result = toRightDown;
			else
				if (leftIsEmpty)
					result = toRightLeft;
				else
					result = toRight;
		else
			if (downIsEmpty)
				if (leftIsEmpty)
					result = toDownLeft;
				else
					result = toDown;
			else
				if (leftIsEmpty)
					result = toLeft;
				else
					result = toNone;


	return result;
}

bool Rectangle::nothingOnRow (const Coordinate& c) const
{
	emptyDirection eD = emptyLine(c);
	return (eD == toRightLeft || eD == toUpRightLeft
		|| eD == toRightDownLeft || eD == toUpRightDownLeft);
}

bool Rectangle::nothingOnCol (const Coordinate& c) const
{
	emptyDirection eD = emptyLine(c);
	return (eD == toUpDown || eD == toUpRightDown
		|| eD == toUpDownLeft || eD == toUpRightDownLeft);
}

bool Rectangle::overlaps (const Rectangle& other) const
{
	bool result = true;
	if (this->getTop() > other.getBottom() || this->getBottom() < other.getTop()
			|| this->getLeft() > other.getRight() || this->getRight() < other.getLeft())
		result = false;
	return result;
}

void Rectangle::print(void) const
{
	this->ul.print();
	this->lr.print();
}

void Rectangle::printDetail(void) const
{
	this->ul.print();
	this->lr.print();
	cout << endl << "Coordinates in this rectangle object: ";
	//vector<Coordinate>::iterator it = this->coos.begin();
	for (int i = 0; i < (int) this->coos.size(); i++)
		this->coos[i].print();
	//cout << endl;
}

void Rectangle::clearCoos (void)
{
	this->coos.clear();
}

Rectangle& Rectangle::operator= (const Rectangle& other)
{
	if (this == &other)
		return *this;

	this->coos = other.coos;
	this->lr = other.lr;
	this->ul = other.ul;
	return *this;
}

Rectangle::Rectangle(const vector<Coordinate>& coos)
{
	//no LongInstance is defined here.
	this->coos = coos;
	if (coos.empty())
	{
		this->setLR(Coordinate(0,0));
		this->setUL(Coordinate(0,0));
		return;
	}

	if ((int) coos.size() == 1)
	{
		this->setLR(coos[0]);
		this->setUL(coos[0]);
		return;
	}

	this->lr = coos[0];
	this->ul = coos[0];

	Coordinate currentCoo;
	
	for (int i = 1; i < static_cast<int>(coos.size()); i++)
	{
		currentCoo = coos[i];
		if (currentCoo.row < this->getTop())
			this->setTop(currentCoo.row);
		if (currentCoo.row > this->getBottom())
			this->setBottom(currentCoo.row);
		if (currentCoo.col < this->getLeft())
			this->setLeft(currentCoo.col);
		if (currentCoo.col > this->getRight())
			this->setRight(currentCoo.col);
	}
}

